#include "uloopplaywidget.h"
#include "upushbutton.h"

#include <QPainter>
#include <QPainterPath>
#include <QParallelAnimationGroup>
#include <QPropertyAnimation>
#include <QTimer>
#include <QDebug>
#include <QHBoxLayout>

/**
* @brief UImageItem
* 图片卡片
*/
class UImageItem : public QWidget
{
public:
    UImageItem(QWidget* parent = Q_NULLPTR);
    ~UImageItem() = default;

    void setImage(const QImage &image);

    void setOpenMirrorImage(bool isOpen);

protected:
    void paintEvent(QPaintEvent* event) Q_DECL_OVERRIDE;

private:
    QImage m_image;
    bool m_isOpen = false;
};

UImageItem::UImageItem(QWidget* parent) : QWidget(parent)
{
}

void UImageItem::setImage(const QImage &image)
{
    m_image = image;
    update();
}

void UImageItem::setOpenMirrorImage(bool isOpen)
{
    if (m_isOpen != isOpen)
    {
        m_isOpen = isOpen;
        update();
    }
}

void UImageItem::paintEvent(QPaintEvent* event)
{
    if (m_image.isNull())
    {
        QWidget::paintEvent(event);
        return;
    }

    QPainter painter(this);
    painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
    painter.setOpacity(isEnabled() ? 1.0 : 0.4);

    QRect pixRect = this->rect();
    if (m_isOpen)
    {
        pixRect.setHeight(pixRect.height() / 4 * 3);
    }
    painter.save();
    QPainterPath path;
    path.addRoundedRect(pixRect, 8, 8);
    painter.setClipPath(path);
    painter.drawImage(pixRect, m_image);
    painter.restore();

    if (!m_isOpen)
    {
        return;
    }
    painter.setOpacity(0.3);
    QImage mirrorImage = m_image.mirrored();
    QRect mirrorRect = QRect(pixRect.x(), pixRect.y() + pixRect.height(), pixRect.width(), pixRect.height() / 4);
    painter.drawImage(mirrorRect, mirrorImage);
}

class ULoopPalyWidgetPrivate
{
public:
    ULoopPalyWidgetPrivate(ULoopPalyWidget* q) : q_ptr(q) {}
    ~ULoopPalyWidgetPrivate() {}

    void init();

    void initConnection();

    void updateItemGeometry();

    void resetImageItems();

private:
    Q_DECLARE_PUBLIC(ULoopPalyWidget)
    Q_DISABLE_COPY(ULoopPalyWidgetPrivate)
    ULoopPalyWidget* const q_ptr = nullptr;

    QSize itemSize = QSize(420, 300);
    QList<QRect> rectList;
    QList<UImageItem*> labelList;
    QMap<UImageItem*, QPropertyAnimation*> animationMap;

    QTimer* timer = nullptr;
    QParallelAnimationGroup* group = nullptr;
    UPushButton* left = nullptr;
    UPushButton* right = nullptr;
};

void ULoopPalyWidgetPrivate::init()
{
    Q_Q(ULoopPalyWidget);

    group = new QParallelAnimationGroup(q);
    timer = new QTimer(q);
    timer->setInterval(3000);

    QHBoxLayout* mainLayout = new QHBoxLayout;
    left = new UPushButton(q);
    left->setButtonType(ButtonType::ButtonNoBackground);
    left->setFixedSize(36, 36);
    left->setIcon(QIcon::fromTheme("left"));
    right = new UPushButton(q);
    right->setButtonType(ButtonType::ButtonNoBackground);
    right->setFixedSize(36, 36);
    right->setIcon(QIcon::fromTheme("right"));
    mainLayout->addWidget(left);
    mainLayout->addStretch();
    mainLayout->addWidget(right);
    q->setLayout(mainLayout);

    initConnection();
}

void ULoopPalyWidgetPrivate::initConnection()
{
    Q_Q(ULoopPalyWidget);

    QObject::connect(timer, &QTimer::timeout, q, &ULoopPalyWidget::onBackwardAnimation);

    QObject::connect(left, &UPushButton::clicked, q, &ULoopPalyWidget::onForwardAnimation);

    QObject::connect(right, &UPushButton::clicked, q, &ULoopPalyWidget::onBackwardAnimation);
}

void ULoopPalyWidgetPrivate::updateItemGeometry()
{
    Q_Q(ULoopPalyWidget);

    QPoint center = q->rect().center();
    int width = itemSize.width() * 1.4;
    QPoint point(center.x() - width / 2, center.y() - itemSize.height() * 0.3);
    QSize size1(itemSize.width() * 0.8, itemSize.height() * 0.8);
    QSize size2(itemSize.width() * 0.6, itemSize.height() * 0.6);

    rectList.clear();
    rectList << QRect(point, size2) << QRect(QPoint(point.x() + itemSize.width() * 0.1, point.y() - itemSize.height() * 0.1), size1)
             << QRect(QPoint(point.x() + itemSize.width() * 0.2, point.y() - itemSize.height() * 0.2), itemSize)
             << QRect(QPoint(point.x() + itemSize.width() / 2, point.y() - itemSize.height() * 0.1), size1)
             << QRect(QPoint(point.x() + itemSize.width() * 0.8, point.y()), size2);

    for (int i = 0; i < labelList.size(); ++i)
    {
        if (nullptr == labelList[i])
        {
            continue;
        }
        if (i > 4)
        {
            labelList[i]->setGeometry(rectList[4]);
        }
        else
        {
            labelList[i]->setGeometry(rectList[i]);
        }
    }
}

void ULoopPalyWidgetPrivate::resetImageItems()
{
    if (!labelList.isEmpty())
    {
        timer->stop();
        const QList<UImageItem*> labelListCopy = labelList;
        labelList.clear();
        for (int i = labelListCopy.count() - 1; i >= 0; --i)
        {
            UImageItem* animation = labelListCopy.at(i);
            animation->setParent(nullptr);
            delete animation;
        }
        animationMap.clear();
        group->clear();
    }
}

ULoopPalyWidget::ULoopPalyWidget(QWidget* parent) : UWidget(parent)
    , d_ptr(new ULoopPalyWidgetPrivate(this))
{
    Q_D(ULoopPalyWidget);
    d->init();
}

ULoopPalyWidget::~ULoopPalyWidget()
{
}

void ULoopPalyWidget::setImageList(const QList<QImage> &imageList)
{
    Q_D(ULoopPalyWidget);
    if (imageList.size() < 5)
    {
        qWarning() << "It must greater than 5 images!!!";
    }

    d->resetImageItems();

    for (int i = 0; i < imageList.size(); ++i)
    {
        UImageItem* item = new UImageItem(this);
        d->labelList.append(item);
        QPropertyAnimation* propertyAnimation = new QPropertyAnimation(item, "geometry", this);
        d->animationMap.insert(item, propertyAnimation);
        d->group->addAnimation(propertyAnimation);
        item->setImage(imageList[i]);
    }

    d->labelList.at(0)->stackUnder(d->labelList.at(1));
    d->labelList.at(1)->stackUnder(d->labelList.at(2));
    for (int i = 3; i < imageList.size(); ++i)
    {
        d->labelList.at(i)->stackUnder(d->labelList.at(i - 1));
    }

    d->timer->start();
}

void ULoopPalyWidget::setCenterItemSize(const QSize &size)
{
    Q_D(ULoopPalyWidget);
    d->itemSize = size;
    d->updateItemGeometry();
}

void ULoopPalyWidget::onForwardAnimation()
{
    Q_D(ULoopPalyWidget);
    if ((5 > d->labelList.size()) || (5 != d->rectList.size()))
    {
        return;
    }

    if (d->group->state() != QAbstractAnimation::Stopped)
    {
        return;
    }

    auto item = d->labelList.takeFirst();
    d->labelList.append(item);
    for (int i = 0; i < d->labelList.size(); ++i)
    {
        auto label = d->labelList[i];
        if (nullptr == label)
        {
            continue;
        }
        d->animationMap[label]->setStartValue(label->geometry());
        d->animationMap[label]->setEndValue((i > 4) ? label->geometry() : d->rectList[i]);
        d->animationMap[label]->setEasingCurve(QEasingCurve::Linear);
    }
    d->group->start();
    d->labelList.at(2)->raise();
    d->labelList.at(4)->lower();
}

void ULoopPalyWidget::onBackwardAnimation()
{
    Q_D(ULoopPalyWidget);
    if ((5 > d->labelList.size()) || (5 != d->rectList.size()))
    {
        return;
    }

    if (d->group->state() != QAbstractAnimation::Stopped)
    {
        return;
    }

    auto item = d->labelList.takeLast();
    d->labelList.prepend(item);
    for (int i = 0; i < d->labelList.size(); ++i)
    {
        auto label = d->labelList[i];
        if (nullptr == label)
        {
            continue;
        }
        d->animationMap[label]->setStartValue(label->geometry());
        d->animationMap[label]->setEndValue((i > 4) ? label->geometry() : d->rectList[i]);
        d->animationMap[label]->setEasingCurve(QEasingCurve::Linear);
    }
    d->group->start();
    d->labelList.at(2)->raise();
    d->labelList.at(0)->lower();
}

void ULoopPalyWidget::resizeEvent(QResizeEvent* event)
{
    Q_D(ULoopPalyWidget);
    d->updateItemGeometry();
    UWidget::resizeEvent(event);
}
